package org.calzz.jajb.converter;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.calzz.jajb.JsonDeserializer;
import org.calzz.jajb.JsonSerializer;
import org.calzz.jajb.io.IWriter;
import org.calzz.jajb.utils.ReflectionUtils;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;

/**
 * @author Marek Brodziak <marek.brodziak@gmail.com>
 * @author Piotr Pejas <piotr.pejas@gmail.com>
 * @since Mar 9, 2008
 */
public class CollectionConverter implements IConverter {
	private Log log = LogFactory.getLog(CollectionConverter.class);
	private Map<Class<?>, Class<?>> _typeCache = new HashMap<Class<?>, Class<?>>();

	/*
	 * (non-Javadoc)
	 * @see org.calzz.jajb.converter.IConverter#canConvert(java.lang.Class)
	 */
	@Override
	public boolean canConvert(Class<?> clazz) {
		return ReflectionUtils.isInstanceOf(clazz, Collection.class);
	}

	/*
	 * (non-Javadoc)
	 * @see org.calzz.jajb.converter.IConverter#marshal(java.lang.Object,
	 *      java.lang.String, org.calzz.jajb.io.IWriter,
	 *      org.calzz.jajb.JsonSerializer)
	 */
	@Override
	public void marshal(Object obj, String name, IWriter writer, JsonSerializer serializer) {
		writer.startArray(name);

		Collection<?> collection = (Collection<?>) obj;
		for(Object o : collection) {
			serializer.process(o, null, writer);
		}

		writer.endArray();
	}

	/*
	 * (non-Javadoc)
	 * @see org.calzz.jajb.converter.IConverter#unmarshal(java.lang.Object,
	 *      java.lang.Class, org.calzz.jajb.JsonDeserializer)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public <T> Object unmarshal(Object env, Class<T> clazz, JsonDeserializer deserializer, MethodParameter methodParam) {
		Collection<Object> out = null;
		clazz = resolveImplementation(clazz);
		try {
			out = (Collection<Object>) clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		Class valueClass = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);

		List<?> in = (List<?>) env;
		for(Object o : in) {
			out.add(deserializer.process(o, valueClass, methodParam));
		}
		return out;
	}

	@SuppressWarnings("unchecked")
	private Class resolveImplementation(Class<?> clazz) {
		Class<?> implementationType = _typeCache.get(clazz);

		if (implementationType == null) {
			if (clazz.isInterface()) {
				if (Collection.class.equals(clazz)) {
					implementationType = LinkedList.class;
				} else if (List.class.equals(clazz)) {
					implementationType = LinkedList.class;
				} else if (Set.class.equals(clazz)) {
					implementationType = HashSet.class;
				} else if (SortedSet.class.equals(clazz)) {
					implementationType = TreeSet.class;
				} else {
					//cannot find the implementation
					log.error("Cannot find proper implementation of " + clazz.getName());
					throw new RuntimeException("Cannot find proper implementation of " + clazz.getName());
				}
			} else {
				implementationType = clazz;
			}

			if (implementationType != null) {
				_typeCache.put(clazz, implementationType);
			}
		}

		return implementationType;
	}

}
